#version 430 core
#include "DVR_Common.glsl"

//precision highp float;

struct SListNode
{
	uint Next;
	uint PackedColor;
	uint Depth;
};

layout(binding = 0, r32ui) uniform uimage2D u_ListHeadPtrTex;
layout(binding = 0, offset = 0) uniform atomic_uint u_ListNodeCounter;
layout(binding = 0, std430) buffer LinkedLists { SListNode NodeList[]; };

in vec3 v2f_RayAABBEntry;
in vec3 v2f_AABBOffset;

uniform int u_MaxListNodes;
uniform vec3 u_BrickSize;
uniform vec3 u_CameraPosInModel;

vec3 VolumeMin = vec3(-0.5f);
vec3 VolumeMax = vec3(0.5f);

vec2 computeIntersectionWithAABB(const in vec3 vEntry, const in vec3 vDirection, const in vec3 vMin, const in vec3 vMax) 
{
	vec3 RayDir = modifyVecNE2Zero(vDirection);

	vec3 DirectionInverse = 1.0f / RayDir;
	vec3 tMin = (vMin - vEntry) * DirectionInverse;	//the plane ray entrys
	vec3 tMax = (vMax - vEntry) * DirectionInverse;	//the plane ray exits
  	vec3 t1 = min(tMin, tMax);	//prevent the direction of ray from being reversed
	vec3 t2 = max(tMin, tMax);
	float tNear = max(max(t1.x, t1.y), t1.z);
	float tFar = min(min(t2.x, t2.y), t2.z);

	return vec2(tNear, tFar);
}

ivec2 computeStepRange(vec3 vVolumeNear, vec3 vStepSize, vec3 vDirection) 
{
	vec3 AABBMin = v2f_AABBOffset - u_BrickSize / 2;
	vec3 AABBMax = v2f_AABBOffset + u_BrickSize / 2;
	vec2 AABBIntersection = computeIntersectionWithAABB(v2f_RayAABBEntry, vDirection, AABBMin, AABBMax);

	vec3 AABBNear = AABBIntersection.x * vDirection + v2f_RayAABBEntry;
	vec3 AABBFar = AABBIntersection.y * vDirection + v2f_RayAABBEntry;

	int StepStart = int(ceil(distance(AABBNear, vVolumeNear) / length(vStepSize)));
	int StepEnd = int(ceil(distance(AABBFar, vVolumeNear) / length(vStepSize)));

	return ivec2(StepStart, StepEnd);
}

//TODO: Read from gl_FragCoord will cause fragment shader recompilation according to OpenGL debug context.
void storeNode(vec4 vColor) 
{
	uint Index = atomicCounterIncrement(u_ListNodeCounter) + 1u; // 0 represent end, so add 1 to avoid this situation.
	if (Index < u_MaxListNodes) 
	{
		uint OldHead = imageAtomicExchange(u_ListHeadPtrTex, ivec2(gl_FragCoord.xy), Index);
		uint PackedColor = packUnorm4x8(vColor);
		uint Depth = floatBitsToUint(gl_FragCoord.z);
		NodeList[Index] = SListNode(OldHead, PackedColor, Depth);
	}
}

void main()
{
	vec3 Direction = normalize(v2f_RayAABBEntry - u_CameraPosInModel);
	
	vec2 VolumeIntersection = computeIntersectionWithAABB(v2f_RayAABBEntry, Direction, VolumeMin, VolumeMax);
	vec3 VolumeNear = VolumeIntersection.x * Direction + v2f_RayAABBEntry;
	vec3 VolumeFar = VolumeIntersection.y * Direction + v2f_RayAABBEntry;
	
	int NumSteps = 0; //unuse
	vec3 StepSize = computeStepSize(VolumeNear, VolumeFar, Direction, NumSteps);
	ivec2 StepRange = computeStepRange(VolumeNear, StepSize, Direction);

	vec3 Origin = VolumeNear + 0.5f;
	storeNode(accumulateColor(Origin, StepRange.x, StepRange.y, StepSize));
}